﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Cofe.Core.Property;
using Cofe.Core.Utils;
using Cofe.Core.Validater;

namespace Cofe.Core
{
    public static class IPropertyBehaviorManagerExtension
    {
        public static PropertyPair GetProperty(this IPropertyBehaviorManager manager, object property,
            PropertyRetrivalOptions options = PropertyRetrivalOptions.Default)
        {
            return AsyncUtils.RunSync(() => manager.GetPropertyAsync(property, options));
        }


        public static Task SetPropertyAsync(this IPropertyBehaviorManager manager, object property, object value,
            PropertyRetrivalOptions options = PropertyRetrivalOptions.Default)
        {
            PropertyPair pp = PropertyPair.FromValue(property, value);
            return manager.SetPropertyAsync(pp, options);
        }
        public static void SetPropertyPair(this IPropertyBehaviorManager manager, PropertyPair ppair, 
            PropertyRetrivalOptions options = PropertyRetrivalOptions.Default)
        {
            if (!(ppair.IsValid))
                throw new ArgumentException(ppair.GetValidationResult().First().ErrorMessage);
            AsyncUtils.RunSync(() => manager.SetPropertyAsync(ppair, options));
        }

        public static void SetProperty(this IPropertyBehaviorManager manager, object property, object value,
            PropertyRetrivalOptions options =  PropertyRetrivalOptions.Default)
        {
            PropertyPair pp = PropertyPair.FromValue(property, value);
            manager.SetPropertyPair(pp, options);
        }

        public static T GetProperty<T>(this IPropertyBehaviorManager manager, object property)
        {
            return manager.GetProperty(property).ValueAs<T>();
        }

        public static T TryGetProperty<T>(this IPropertyBehaviorManager manager, object property)
        {
            try
            {
                return manager.GetProperty(property).ValueAs<T>();
            }
            catch
            {
                return default(T);
            }
        }

        public static async Task<T> GetPropertyAsync<T>(this IPropertyBehaviorManager manager, object property)
        {
            return (await manager.GetPropertyAsync(property)).ValueAs<T>();
        }


        public static IEnumerable<object> GetSupportedProperties(this IPropertyBehaviorManager manager)
        {
            return Task<IEnumerable<object>>.Run(async () => await manager.GetSupportedPropertyAsync()).Result;
        }


        public static bool CanCreate(this IPropertyBehaviorManager manager)
        {
            return manager.IsPropertySupported(CofeDirectoryProperties.CreateAsync);
        }

        public static bool CanDelete(this IPropertyBehaviorManager manager)
        {
            return manager.IsPropertySupported(CofeProperties.DeleteAsync);
        }

        /// <summary>
        /// This analyse the property's ActionType attribute and call the proper Invoke/InvokeT method accordingly.
        /// </summary>
        /// <param name="property"></param>
        /// <param name="parameters"></param>
        /// <param name="outputType"></param>
        /// <param name="output"></param>
        public static void InvokeNonGeneric(this IPropertyBehaviorManager manager, object property, ParameterDic parameters, 
            out Type outputType, out object output)
        {
            ActionAttribute actionAttribute = AttributeUtils<ActionAttribute>.FindAttribute(property);
            if (actionAttribute == null)
                throw new NotSupportedException(property.ToString() + " not an action.");

            outputType = actionAttribute.OutputType;
            output = null;
            if (outputType == null)
                manager.Invoke(property, parameters);
            else
            {
                var invokeMethod = typeof(IPropertyBehaviorManager).GetMethods()
                    .First(mi => mi.Name == "Invoke" && mi.IsGenericMethod).MakeGenericMethod(outputType);
                output = invokeMethod.Invoke(manager, new object[] { property, parameters });
            }
        }
    }
}
